package screenpac.ghosts;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import screenpac.extract.Constants;
import screenpac.features.NodeScore;
import screenpac.features.Utilities;
import screenpac.model.GameStateInterface;
import screenpac.model.GhostState;
import screenpac.model.MazeInterface;
import screenpac.model.Node;

public class GaiSuperTeam implements GhostTeamController, Constants{

    static int off = 75;
    /*static int[][] da = {
    	{off, 0}, 
    	{0, off}, 
    	{-off, 0}, 
    	{0, -off}};*/
    
    static int[][] da = {
    	{0, -off},
    	{off, 0},
    	{0, off}, 
    	{-off, 0}};

    int[] dirs;
    ArrayList<Node> junctions;
    MazeInterface maze;
    
    //Luis:
    public enum Ghosts {Blinky, Inky, Pinky, Sue};
    public static final int BLINKY = 0;
    public static final int INKY = 1;
    public static final int PINKY = 2;
    public static final int SUE = 3;

    public GaiSuperTeam() {
        this.dirs = new int[nGhosts];
        // set up the junctions

    }

    public int[] getActions(GameStateInterface gs) {
        /*
    	// work out where each ghost should be
        // in order to pincer the Pac-Man
        // find the closest junctions to pac-man
        // in each direction
        if (!gs.getMaze().equals(maze)) setMazeIntersections(gs.getMaze());
        // now calculate an aim point for each ghost
        // by finding the closest junction within a certain
        // distance of the pac-man in each direction
        Node pac = gs.getPacman().current;
        Set<Node> targets = new HashSet<Node>();
        targets.addAll(junctions);
        for (int i=0; i<nGhosts; i++) {
            GhostState ghost = gs.getGhosts()[i];
            Node next;
            ArrayList<Node> possibles = ghost.getPossibles();
            if (possibles.size() == 1) {
                // if there's only one then set the direction for it
                next = possibles.get(0);
            } else {
                // aim for the junction closest to the aim point
                Node target = getClosest(targets, new AimPoint(pac.x + da[i][0], pac.y+ da[i][1]));
                // now select the next node as the *possible* node with
                // the smallest shortest path distance
                next = Utilities.getClosest(possibles, target, gs.getMaze());
                targets.remove(target);
            }
            dirs[i] = Utilities.getWrappedDirection(ghost.current, next, gs.getMaze());           
        } 
        */
    	blinky(gs);
    	inkyPinky(gs);
    	sue(gs);
        return dirs;
    }
    
    /**
     * BLINKY: Get the closest path to pacman
     * @param gs
     */
    private void blinky(GameStateInterface gs) {
    	ArrayList<Node> options = new ArrayList<Node>();
    	NodeScore pathScore = new PathScore();
    	GhostState gh = gs.getGhosts()[BLINKY];
        options.clear();
        for (Node n : gh.current.adj) {
            // turning back is not a valid option
            if (!n.equals(gh.previous)) options.add(n);
        }
        dirs[BLINKY] = Utilities.getMinDir(options, gh.current, pathScore, gs);
    }
    
    /**
     * Inky and Pinky go to the closest 2 intersections to pacman randomly
     * @param gs
     */
    private void inkyPinky(GameStateInterface gs) {
        if (!gs.getMaze().equals(maze)) setMazeIntersections(gs.getMaze());
        Node pac = gs.getPacman().current;
        Set<Node> targets = new HashSet<Node>();
        targets.addAll(junctions);
        for (int i=INKY; i<=PINKY; i++) {
            GhostState ghost = gs.getGhosts()[i];
            Node next;
            ArrayList<Node> possibles = ghost.getPossibles();
            if (possibles.size() == 1) {
                // if there's only one then set the direction for it
                next = possibles.get(0);
            } else {
                // aim for the junction closest to the aim point
            	//Random 0 - 3
            	int pacDir = gs.getPacman().curDir;
            	Node target;
            	if(pacDir >= 0  && pacDir <= 3) {
            		target = getClosest(targets, new AimPoint(pac.x + da[pacDir][0], pac.y+ da[pacDir][1]));
            	} else {
            		int random = rand.nextInt(nGhosts);
                    target = getClosest(targets, new AimPoint(pac.x + da[random][0], pac.y+ da[random][1]));
            	}
            	
            	switch(pacDir) {
            	
            	case 0: //UP
            		
            		break;
            		
            	case 1: //RIGHT
            		
            		break;
            		
            	case 2: //DOWN
            		
            		break;
            		
            	case 3: //LEFT
            		
            		break;
            		
            		default: //NEUTRAL
            			
            			break;
            	}
            	
            	//int random = rand.nextInt(nGhosts);
                //Node target = getClosest(targets, new AimPoint(pac.x + da[random][0], pac.y+ da[random][1]));
                // now select the next node as the *possible* node with
                // the smallest shortest path distance
                next = Utilities.getClosest(possibles, target, gs.getMaze());
                targets.remove(target);
            }
            dirs[i] = Utilities.getWrappedDirection(ghost.current, next, gs.getMaze());           
        } 
    }
    
    /**
     * Sue : random at this moment
     * @param gs
     */
    private void sue(GameStateInterface gs) {
    	/*ArrayList<Node> options = new ArrayList<Node>();
    	NodeScore pathScore = new RandScore();
    	GhostState gh = gs.getGhosts()[SUE];
        options.clear();
        for (Node n : gh.current.adj) {
            // turning back is not a valid option
            if (!n.equals(gh.previous)) options.add(n);
        }
        dirs[SUE] = Utilities.getMinDir(options, gh.current, pathScore, gs);
        */
    	
    	ArrayList<Node> options = new ArrayList<Node>();
    	NodeScore euclideanScore = new EuclideanScore();
    	GhostState gh = gs.getGhosts()[SUE];
        options.clear();
        for (Node n : gh.current.adj) {
            // turning back is not a valid option
            if (!n.equals(gh.previous)) options.add(n);
        }
        dirs[SUE] = Utilities.getMinDir(options, gh.current, euclideanScore, gs);
    	
    }

    private void setMazeIntersections(MazeInterface maze) {
        this.maze = maze;
        // set up the junctions
        junctions = new ArrayList<Node>();
        for (Node n : maze.getMap()) {
            if (n.adj.size() > 2) {
                junctions.add(n);
            }
        }
    }

    public Node getClosest(Collection<Node> nodes, AimPoint p) {
        double best = Double.MAX_VALUE;
        Node sel = null;
        for (Node n : nodes) {
            double s = p.score(null, n);
            if (s < best) {
                best = s;
                sel = n;
            }
        }
        return sel;
    }
}